Skip to content

feat: converge template identity, runtime contracts, and release state - #400

Merged
speak-agent merged 44 commits into
mainfrom
feat/template-runtime-graphics-aur
Aug 9, 2026
Merged

feat: converge template identity, runtime contracts, and release state#400
speak-agent merged 44 commits into
mainfrom
feat/template-runtime-graphics-aur

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Aug 9, 2026

Copy link
Copy Markdown
Member

概要

本 Draft PR 实现 #398 冻结的聚焦方案(包/模板身份、事务式项目创建、根本地运行时、图形栈 provenance、release desired state、mcpp-bin AUR 恢复),并在收尾阶段按
.agents/docs/2026-08-10-pr400-completion-design.md
的 D1–D5 全量补齐,同时纳入 #401

核心产品决策:

  1. 包和模板统一使用 [ns.]name[@version][:tname];省略 ns 严格等于 mcpplibs;不引入 --variant;唯一模板自动成为默认模板。
    • 省略 namespace 的依赖有一个版本的过渡期:精确坐标未命中时仍可到达 compat.<name> 与「不声明 namespace 的上游 descriptor」,但必须打印弃用警告、给出可粘贴的 manifest 片段,并把规范身份写入 lock/install/cache。2026.9 移除。
  2. xlings/xim 负责图形栈、运行时 provider 和 sentinel;mcpp-index 描述 C++ 包依赖;mcpp 不探测 GPU、Mesa、NVIDIA、WSL、driver 或 ICD。
  3. mcpp 默认使用自己的运行时;根项目/workspace 根可在 mcpp.toml 中选择 xlings SubOS。SubOS 不传递,无 CLI override。
  4. AUR 自动化只管理 mcpp-bin,且自动触发默认不发布,需显式设置仓库变量 AUR_AUTOPUBLISH 才布防。

已实现

  • 共享、精确、命名空间感知的 PackageSelector;exact miss 只给提示,不跨命名空间静默 fallback
  • 裸名过渡期:一个版本的出口坡道,带完整身份的弃用警告 + mcpp add 自动迁移 manifest
  • 精确 TemplateSpec、稳定版本选择、唯一模板自动默认和确定性多模板错误
  • 事务式 mcpp new:安全项目名、单次渲染、完整校验、rollback、跨平台 no-replace commit
  • 根/workspace-root RuntimeSelection 和不可变 RuntimeBinding,build/run/test/fast path 共用同一快照
  • Linux ELF/runtime closure:精确 glibc payload、Rule A/Rule B、sticky verdict、stored doctor
  • Bug: 私有 glibc 2.44 的 libc.so.6 引用 loader 私有符号 __pointer_chk_guard,mcpp run 下子进程(/bin/sh)必死 #401:私有 glibc 退出进程环境,收敛为新模块 src/platform/runtime_env_contract.cppm 的作用域决策
  • provider-neutral runtime requirement/provider/artifact/provenance 和 LinkIntent;mcpp why runtime 不重新探测 host
  • 不可变 schema-1 mcpp-release.json,最终公开 inventory 重取和 payload checksum 复算
  • mcpp-bin-only AUR reconciler:event/schedule/manual、Arch vercmp、幂等/单调、checksum、fast-forward-only、有界恢复;自动触发需布防
  • exact miss 的隐私安全 route: 诊断
  • e2e fixture 路径卫生:host_path helper + lint,修掉 24 个文件里 40 处 shell 拼写路径
  • 版本 2026.8.10.1、英文/中文用户和发布文档

收尾阶段的三个判定(详见 checkpoint

1. Windows E2E 2/2 的根因此前判错了

不是 workspace 索引继承(step 14),而是 step (15) 的 fixture 把 MSYS 路径 $TMP/... 写进了 mcpp.toml。原生 mcpp.exe 把开头的 / 读成「当前盘根相对」,索引落到 C:\tmp\…

四条独立证据:退出码是 2(mcpp 自己的码经 set -e 直传;step 14 被 || { …; exit 1; } 包住只能是 1);step 14 的失败横幅不在日志里;失败前最后两条未捕获输出属于 step (5)/(12);step (15) 是本分支新增的,而 main 的 Windows E2E 是绿的。

Wine 重放是假绿:Wine 把 Z: 映射到 /\tmp\… 落回真实 /tmp/…

2. ed4cf64 的 anchoring 改动:已证明,保留

改动前后失败文本逐字相同,说明它没修好任何东西。按 D2 做了真实 RED:换回 weakly_canonical 会让 unit/test_pm_index_route 失败(71 passed, 1 failed)—— anchoring 必须把 index 留在作者声明的那棵树里,而经符号链接访问的 workspace 会被重定位。改动保留并由该用例锁住;同时把「断言路径拼写」换成能力断言。

3. 裸名精确化是对已发布数据的破坏性变更

四个红 job 全是 ftxui exact miss。本仓自己的 mcpp.toml 也被迫改才能自举,而索引里 34 个 compat.* 包与所有既有用户 manifest 都写裸名 —— 修 xlings 帮不到他们。因此加了迁移窗口(见上),xlings #521 不再在发布关键路径上

当前阻塞:openxlings/xlings#524

按要求把 xlings pin 提升到最新 2026.8.10.1 后,4/4 Linux job 全红:冷 home 装不上 xim:gcc@16.1.0 —— 依赖解析下载 glibc 2.44,而 gcc 的 config hook 找不到该 payload。

同一 workflow、同一 runner 镜像、两次都确认 cache miss 的 A/B:

xlings 冷缓存 结果
2026.8.9.2 ✅ 确认 cache miss gcc 安装成功(run 31317627461
2026.8.10.1 ✅ 确认 cache miss 上述失败(run 31335075557,4/4 Linux job)

所以不是「一直坏、被热缓存掩盖」。已带证据上报 openxlings/xlings#524;mcpp 侧暂时把 pin 停在 2026.8.9.2,等修复发布后再提升(独立 commit)。

验证证据

  • 完整 C++ unit/integration:73/73
  • test_aur_reconcile + test_release_manifest25/25
  • check_version_pins.sh、全部 workflow YAML 解析、git diff --check、隐私扫描:通过
  • 逐条通过的新增/改动 e2e:000912273242434462162208209210
  • Bug: 私有 glibc 2.44 的 libc.so.6 引用 loader 私有符号 __pointer_chk_guard,mcpp run 下子进程(/bin/sh)必死 #401 实测对照(同一 fixture):已发布 mcpp LDLP=[…/runtime:…/xim-x-glibc/2.39/lib64];本分支 LDLP=[…/runtime]
  • 最新 HEAD 的完整 Linux e2e 与全矩阵 CI 终态未取得前,不把任何一项记为通过

文档

尚未完成

Closes #398, #401.

Related: #380, #392, #396, #397, mcpplibs/mcpp-index#197, openxlings/xlings#521, openxlings/xlings#524

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — exact local-index namespace inheritance (e6050b7)

  • Fixed exact dependency lookup so a name-only descriptor inherits the namespace of its owning declared index, including [indices] default.
  • Kept selector semantics strict: this does not restore cross-namespace fallback.
  • Added a focused regression test for acme.util resolving a descriptor that declares only name = "util".
  • Verification: PmIndexRoute.* passed 8/8; the complete unit-test invocation reported 72/72 test binaries passing.
  • Privacy scan and git diff --check passed; no machine-local paths or user identifiers were committed.

Next checkpoint will migrate the affected E2E fixtures and validate the previously failing local-index scenarios against this commit.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — exact identity retained through the build resolver (d008a21)

The first fix covered shared IndexRoute lookup, but the build path performed a second identity check without the owning-index namespace. This commit carries the same index ownership into that stricter check, so name-only descriptors in local-dev or redirected default indices remain exact identities instead of false misses.

Evidence:

  • Rebuilt the Draft-PR CLI from this source.
  • E2E 49 advanced from no package found for exact selector through resolution and compilation.
  • With the loader-physics correction in the next isolated commit, E2E 49 completes successfully.
  • Diff/privacy checks passed; no local paths or user identifiers are included.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — ELF loader physics / multi-glibc correctness (c569152)

The closure validator was resolving every dependency's libc.so.6 independently through that object's RUNPATH. That can report two payloads even though the ELF loader reuses the first process-global SONAME mapping.

This commit now:

  • preserves DT_NEEDED order,
  • parses DT_SONAME,
  • tracks already-loaded SONAME mappings (including PT_INTERP), and
  • reuses the mapped object before consulting a later dependency's RUNPATH.

Verification:

  • New synthetic ELF regression was red before the fix (resolvedLibcs.size() == 2) and green after it (== 1, selected payload retained).
  • Full test_elf_runtime: 11 passed, 1 platform-boundary skip on Linux.
  • Existing proven two-libc mismatch and Rule A/B rejection tests remain passing.
  • E2E 49 now completes successfully (OK) with the real selected runtime/toolchain combination.
  • Diff/privacy checks passed; no local machine paths or identifiers were committed.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Validation checkpoint — local-index exact identity group

Against current Draft-PR HEAD c569152, the previously failing local-index/cache scenarios now pass 7/7:

  • 49 BMI cache with nested custom index
  • 51 package-generated files
  • 121 default-namespace index redirect
  • 147 per-OS xpkg features
  • 172 cross-project build cache
  • 174 cache modes and commands
  • 184 build-cache object layout

These use isolated fresh sandboxes and the rebuilt PR CLI. No test fixtures needed loosening: the implementation fixes make their existing exact selectors resolve as intended.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — E2E contract migrations (679c7ab)

Three tests were asserting pre-exact-identity or host-capability behavior rather than product regressions:

  • 100 now selects compat.cjson explicitly in both manifests.
  • 123 asserts the namespace-qualified dependency object root (obj/mcpplibs_mydep/...).
  • 204 only exercises symlink rejection when the filesystem actually produced a symlink; restricted Windows shells that return success without one no longer create a false failure.

Local verification with the rebuilt PR CLI: 3/3 passed (100, 123, 204). Diff/privacy checks passed.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — fake xlings fixtures retain truthful SubOS contracts (65fc77b)

E2E 52 and 58 used a fake xlings self init that wrote {} as the SubOS manifest. The new runtime contract correctly rejects that as an environment with no declared runtime/provider facts.

The fixtures now copy the already inherited native runner's default SubOS contract into the fake home. This keeps the fake focused on install ordering/index transport while preserving truthful platform/runtime facts.

Verification: 52 and 58 pass 2/2 with the rebuilt PR CLI. Diff/privacy checks passed; the committed tests contain no host-specific contract or path.

@Sunrisepeak

Copy link
Copy Markdown
Member Author

Checkpoint — exact misses retain an unreadable-index cause (e3b93ac)

The exact-selector failure path bypassed the existing with_index_cause wrapper, so an E0006 floor violation was printed early but the terminal error still blamed a missing package. The final exact-miss diagnostic now carries the recorded unreadable-index cause.

E2E 185 is green across all three gates:

  • terminal failure names E0006 / unreadable index,
  • a too-new unused index remains isolated,
  • MCPP_INDEX_FLOOR=ignore still works.

Diff/privacy checks passed.

@speak-agent

Copy link
Copy Markdown
Member

隐私身份 checkpoint

已由 speak-agent 推送独立 commit cf39cb2fix(aur): use privacy-safe release identity

问题与根因:完整 PR added-line/privacy 复核发现,AUR reconciler 的提交身份仍硬编码了个人邮箱;mcpp-bin/PKGBUILD maintainer 行也继续引用该地址。交接 Markdown 另有 3 行尾随空格。

RED:新增 public service identity contract 后,测试因 AUR_COMMIT_NAME/AUR_COMMIT_EMAIL 不存在而报错;新增 rendered PKGBUILD maintainer 断言后,旧模板失败。

GREEN:

  • reconciler 统一使用 speak-agent + GitHub noreply 地址;
  • mcpp-bin/PKGBUILD maintainer 改为同一公共服务身份;
  • 新增两个回归断言,AUR contract suite 现在 12/12;
  • Python compile、git diff --check、PR added-line 隐私扫描通过;
  • 交接 Markdown 尾随空格已修复;
  • scripts/aur/mcpp-m/** 聚合 SHA256 仍为 afb8a647e04483a86985119e07086016f49d55f177ee6257094c336d226113c6,未修改。

本 commit 和 push 本身也使用 speak-agent GitHub noreply identity。最新 HEAD 为 cf39cb2,latest-head CI 已再次重新排队。

@speak-agent

Copy link
Copy Markdown
Member

文档一致性 checkpoint

已由 speak-agent 推送独立 commit 189c6d1docs: record privacy-safe AUR checkpoint

同步内容:

  • 中文交接更新到隐私修复 checkpoint,补入 9f6161a6e42d6ccf39cb2 的职责;
  • AUR contract 从 11/11 更新为 12/12;
  • 验证账本记录 rendered mcpp-bin 和 reconciler 都必须使用公共 speak-agent noreply 身份;
  • 明确 cf39cb2 之后的 latest-head CI 仍需终态,不提前宣称通过。

git diff --check 与 PR added-line 隐私扫描通过。该 commit 仅同步文档证据,最新 HEAD 为 189c6d1

@speak-agent

Copy link
Copy Markdown
Member

Checkpoint:精确索引路由诊断(9a47ccf

已作为独立提交推送到 Draft PR:

  • mcpp add 在“可确认不存在”的错误中增加稳定的 route: 状态;
  • 本地索引只报告 root/pkgs 的 present/absent,不输出绝对路径或 runner/用户目录;
  • 单测覆盖路径隐私、索引根缺失、workspace 根索引继承;
  • E2E 固化 local index 'acme': root present, pkgs present 契约。

验证证据:

  • RED:生产接口尚未实现时,unit/test_pm_index_route 按预期编译失败;
  • GREEN:mcpp test pm_index_route,12/12;
  • GREEN:tests/e2e/12_add_command.sh,OK;
  • git diff --check 通过;新增行隐私扫描未发现本机目录或私人邮箱。

此提交的目的也是让 Windows 原生 E2E 直接暴露失败属于“workspace 索引未继承 / 根不存在 / pkgs 不存在 / 描述符读取”中的哪一层。当前仍保持 Draft,不把这次推送表述为 Windows 问题已经修复;等待最新 HEAD 的原生 CI 证据后继续做下一笔独立修复。

@speak-agent

Copy link
Copy Markdown
Member

Checkpoint:交接与验证账本同步(64803fc

已把此前能确认但文档尚未同步的状态全部写回 Draft PR:

  • ed4cf64 原生矩阵终态:12 success / 5 failure / 1 cancelled;
  • 四个 xlings 裸 ftxui 失败继续归入 xlings #521;
  • Windows E2E 2/2 是独立 mcpp 失败,记录 run 31321961040 / job 93266267511,不再误写为 pending 或“可能已修复”;
  • 记录同一 Windows unit 通过、精确 artifact 在隔离 Wine 重放通过,但这些不能替代 GitHub Windows 原生结论;
  • 记录 9a47ccf 的 RED/GREEN、无路径泄露诊断,以及下一轮只根据 route: 原生证据修复;
  • commit 追踪表补齐 189c6d19a47ccf,并更新未完成项和接手顺序。

文档提交只同步证据,不改变“Draft、待用户 review、不合并/不发布”的边界。最新 HEAD 已推送;后续以该 HEAD 的 Windows E2E 2/2 为准。

@speak-agent

Copy link
Copy Markdown
Member

Draft PR 正文已同步

已把 64803fc 交接账本中的当前事实同步到 PR 正文:补充隐私安全 route 诊断、ed4cf64 原生矩阵终态、独立 Windows E2E 2/2 失败及其 run/job、Wine/单元测试不能替代原生结论,以及最新尚未完成清单。PR 继续保持 Draft。

@speak-agent

Copy link
Copy Markdown
Member

中文任务交接(最新单入口)

当前交接对象:Draft PR #400,分支 HEAD 64803fce2bfc09e581f0d3f58869be5ae69aeab2。远端与实施 worktree HEAD 一致,worktree clean;相对冻结基线共有 32 个追加式 commit,未 amend/rebase/squash/force-push。

完整交接文档:

已做

  1. 包与模板身份

    • 统一 [ns.]name[@version][:tname];省略 ns 严格等于 mcpplibs,version/tname 可省略,不增加 --variant
    • 唯一模板未声明 default = true 时自动成为默认;多模板无默认给确定性错误。
    • 包选择、manifest、add/remove、scaffold、resolver 收口到 exact identity,禁止跨 namespace 静默 fallback。
  2. mcpp new

    • 项目名在配置/索引/网络访问前验证。
    • 使用同父目录 staging、完整渲染和 manifest 校验、sync、跨平台 no-replace commit;失败 rollback,不留下半成品。
  3. Runtime/SubOS

    • mcpp 默认运行时;只允许根项目/workspace 根 [xlings].subos 选择本地 build/run 环境,暂不提供 CLI override。
    • SubOS 不传递给成员或库依赖;RuntimeSelection/RuntimeBinding 在 build/run/test/fast path 共用。
    • Linux glibc/ELF closure、Rule A/B、sticky verdict、stored doctor 已实现。
  4. 图形栈边界

    • mcpp 不探测 GPU、Mesa、NVIDIA、WSL、driver、ICD。
    • xlings/xim provider/sentinel 负责环境事实;mcpp-index 只表达 C++ 包依赖;mcpp 保存 provider-neutral provenance 并可 why runtime
  5. Release/AUR

    • schema-1 不可变 mcpp-release.json、最终公开 inventory 重取和 checksum 复算。
    • AUR 只管理 mcpp-bin;reconciler 支持 release/schedule/manual、Arch vercmp、幂等/单调、fast-forward-only 和有界恢复。
    • 发布提交身份使用公共 speak-agent noreply 契约;mcpp-m 未改。
  6. 跨仓库

  7. 文档和可追溯性

    • 设计、计划、验证账本、中文交接均已进仓库。
    • 每个逻辑变化独立 commit、立即 push、中文 checkpoint;PR 正文已同步当前真实状态。
    • 最新两笔为 9a47ccf(隐私安全 index route 诊断)和 64803fc(同步 Windows 原生证据与交接)。

遇到的问题与当前判断

  1. xlings 裸依赖

    • 新 exact identity 暴露 xlings source 的裸 ftxui:四个平台 lane 失败。
    • 根因在 xlings 声明,不应在 mcpp 恢复 fallback;修复已在 xlings #521,但尚未 review/合并/发布,现有稳定 2026.8.9.2 不包含它。
  2. Windows workspace root local index

    • ed4cf64 原生 Windows E2E 2/2 在 member 内执行 mcpp add acme.util@2.0.0 失败:run 31321961040 / job 93266267511
    • 同一 Windows unit 通过;同一 artifact 在隔离 Wine 下用手工 member 和 mcpp new m1 都通过,所以 unit/Wine 不能代替 GitHub Windows/Git Bash 证据。
    • 9a47ccf 已增加永久 route: 诊断,仅输出 local index 的 root/pkgs present/absent,不泄露绝对路径。当前最新 Windows run 31324173520 正在执行 E2E,尚无终态,不能宣称已修复。
  3. 其它已处理问题

    • macOS BMI E2E 把平台合法 init/link 工作误当全图不稳定,已缩小到测试真正负责的 edge。
    • Windows fixture 持有打开文件导致清理失败,已收口 stream lifetime。
    • E2E chore(manifest): use TOML-native namespaced dep for mcpplibs.cmdline #156 使用错误的 HOME 根注入 libc poison,已改为实际 MCPP_HOME
    • AUR 曾返回 maintenance;只记录外部失败,没有绕过或重复跑旧 release workflow。

已有验证

  • ed4cf64 完整 C++ unit/integration:72/72。
  • 隔离 Linux 全量 E2E:186 pass / 1 fixture fail / 19 skip;fixture 修复后精确重跑通过,但没有冒充最新 HEAD 206-case 全绿。
  • 9a47ccf:先得到缺少诊断 API 的 RED;GREEN 为 PmIndexRoute 12/12、真实 E2E fix(pm): de-inline resolver functions to satisfy musl-gcc 15.1 link #12 OK。
  • release manifest 12/12;AUR state-machine/contract 12/12。
  • 公开 v2026.8.8.4 replay 字节稳定;真实 Arch .SRCINFO、非 root makepkg --verifysource 和两个 Linux checksum 通过。
  • ed4cf64 原生矩阵终态:12 success / 5 failure / 1 cancelled。四个 failure 属于 xlings #521,一个是上述 Windows E2E;cancelled 不算 PASS。
  • 变更通过 diff/privacy、shell、Python、workflow YAML、git diff --check 检查;没有在公开内容中加入本地路径或个人邮箱。

还没有做

  1. 等当前 latest-head Windows E2E 2/2 输出 route:,按原生证据完成 mcpp 内部修复,并重新验证完整矩阵。
  2. 用户 review xlings #521;明确同意后转 ready、普通合并,再发一个包含修复的新 xlings 稳定版本。
  3. mcpp 独立 commit 更新新 xlings pin,并再跑 latest-head 全矩阵。
  4. 用户 review mcpp feat: converge template identity, runtime contracts, and release state #400;没有明确 review 前不转 ready、不合并。
  5. 普通合并后发布 v2026.8.9.1,核验所有平台资产、sidecar、manifest、公开 inventory/checksum。
  6. GitCode/必要的 xim-pkgindex 跟进、AUR mcpp-bin 真正收敛、隔离 fresh-home/SubOS/多 glibc/OpenGL/Vulkan 全生态验收。
  7. feat: converge template selectors, runtime binding, graphics contract, and mcpp-bin AUR #398 随 PR 合并关闭;bug: mcpp new accepts unsafe names and can hang during template rendering #380/[Linux] mcpp run 无法启动:私有 glibc 2.39 与系统库 GLIBC 版本冲突,以及切换 glibc 2.44 过程中的排查与发现 #392/Build-time physics check for form-X binaries (rules A/B): fail at link time instead of crashing at run time #396 只在发布后二次验收后关闭;残留统一回写 汇总:2026-08-09 全量 issue 核验 —— 关闭说明、合并去向,以及 9 条新发现缺陷的收口 #397
  8. C1-C9 和其它开放 issue/PR 的最终清理仍未完成;特殊保留 NULL (保留) #43留言版 | 使用mcpp工具构建的项目(库/工具/应用) #260 及明确 Draft/do-not-merge 项不动。

接手顺序

  1. 确认 GitHub 身份仍是 speak-agent,并确认本地/远端 HEAD 一致且 clean。
  2. 先读 Windows run 31324173520 的 E2E 2/2 route:;每次修复继续 RED → 最小实现 → GREEN → 独立 commit/push/中文 checkpoint。
  3. 不重复 rerun 已被新 HEAD 取代的 workflow;pending/cancelled/superseded 不算通过。
  4. 完成 Windows 内部问题后,请用户 review xlings #521;按普通路径 merge/release,再更新 mcpp pin。
  5. latest-head 全矩阵和交付账本完整后,请用户 review feat: converge template identity, runtime contracts, and release state #400;未经同意不合并/发布。

强制边界

  • PR 保持 Draft。
  • 不 amend/rebase/squash/force-push,不用 admin/bypass。
  • 每笔改动都要独立 commit、立即 push、中文 PR checkpoint。
  • 不在 commit、文档、PR 正文或评论中放本地用户名、绝对目录、个人邮箱、凭据。
  • mcpp-m 不动;SubOS 不传递;mcpp 不探测 GPU;特殊保留 issue 不动。

The Windows E2E 2/2 failure is not the workspace index inheritance the
last three commits were aimed at. It is step (15) of 12_add_command.sh,
added by this branch, which writes an MSYS path into a manifest:

    TMP=$(mktemp -d)                       # Git Bash -> /tmp/tmp.XXXXXXXX
    acme = { path = "$TMP/myapp/index" }   # written as FILE CONTENT

MSYS converts POSIX paths on the way into argv and the environment; it
does not convert file content. A native mcpp.exe reads the literal
leading `/` as "root of the current drive", so the fixture index
resolves to C:\tmp\... and is absent. That is exactly what the route
diagnostic reported: `local index 'acme': root absent, pkgs absent`.

Four independent signals place the failure at step (15), not (14):
exit code 2 is mcpp's own status forwarded by `set -e`, while step (14)
is wrapped in `|| { …; exit 1; }`; step (14)'s failure banner never
appears; the last uncaptured output before it belongs to step (12); and
step (14) predates this branch, on which the Windows suite was green.

The Wine replay that "reproduced it as passing" was a false negative:
Wine maps Z: to /, so \tmp\... lands back on the real /tmp. Wine can
show a PE runs. It cannot show anything about path semantics.

Fixed as a class rather than a line. `_host_path.sh` provides
`host_path` (cygpath -m on MSYS, identity elsewhere, relative paths
untouched), 00_fixture_path_hygiene.sh fails any fixture that
interpolates a non-`*_HOST` variable into a TOML `path =` value, and the
40 pre-existing sites across 24 files are migrated. Most were masked
only because those tests skip on Windows for a missing capability.
`inherit_workspace_indices` was switched from `weakly_canonical` to
`lexically_normal` in ed4cf64 to fix the red Windows suite. It did not:
the same failure appeared identically before (run 31318089536) and after
(run 31321961040), and the actual cause was a fixture writing an MSYS
path into mcpp.toml. So the change was landed against a misdiagnosis,
against a behaviour that was green on main, with no test able to tell
the two spellings apart.

It turns out to be right for a different reason, and now it says so.
Anchoring answers "which directory did the workspace author mean", and
the answer must stay inside the tree the author addressed.
`weakly_canonical` answers "what is this path once every symlink is
resolved", so a workspace reached through a symlinked parent gets
relocated into a tree the author never wrote — observable, because
prepare reports a missing descriptor as "not found in local index at
'<path>'". Verified as a real RED: restoring `weakly_canonical` fails
`unit/test_pm_index_route` (71 passed, 1 failed).

The existing test also stops asserting a path SPELLING. Comparing the
stored path against `(root / "index").lexically_normal()` passes for any
rule that happens to produce that string and says nothing about whether
the index can be read — which is how it stayed green through a red
Windows suite. It now asserts the capability: route description, then a
descriptor lookup that actually resolves.
Namespace omission means exactly `mcpplibs`. But every published
`compat.*` package and every manifest written before exact identity
spells its dependency bare — `gtest = "1.15.2"`, `ftxui = "6.1.9"` — so
turning that into an immediate hard error makes an mcpp upgrade break
builds against data that is already published and cannot be edited
retroactively. Four CI jobs on this branch are that breakage, and this
repository's own mcpp.toml had to move gtest under
[dev-dependencies.compat] to keep bootstrapping: a change that breaks
its own project is breaking everyone's.

It is the mirror of the rule that keeps a raised index floor from
bricking older clients. Published data must not break the program; a
published program must not break existing data either. Both degrade.

The #278 defect was the SILENCE, not the reach: mcpp continued with a
namespace the user never wrote and never said so. So the rungs come
back, and cannot be quiet. After the exact coordinate misses, and only
for a selector whose namespace was OMITTED, `(compat, name)` and the
namespace-less discovery rung are tried; a hit is announced with the
canonical selector and the exact manifest edit, and the canonical
identity — not the ambiguous spelling — is what reaches the lock, the
install layer and the cache label. `mcpp add` goes further and performs
the edit, so touching a dependency migrates it.

`mcpplibs.gtest` states an identity and still misses. A third-party
namespace is still unreachable by a bare name. Removed in 2026.9, named
once as kBareNameFallbackRemovedIn so the warning, the docs and the
removal cannot drift apart.

Test 162 now asserts both branches and that an already-exact selector is
not nagged, and it serves the compat fixture from its own index instead
of whatever gtest happened to be installed.
)

`mcpp run` put the private glibc directory on LD_LIBRARY_PATH. That
variable is inherited by every process the program ever spawns, and a
child like /bin/sh is loaded by the HOST loader — PT_INTERP is baked in
and no environment variable overrides it. glibc's libc.so.6 and its
ld.so are version-locked through GLIBC_PRIVATE, so the child dies during
relocation, before main:

  sh: symbol lookup error: …/xim-x-glibc/2.44/lib64/libc.so.6:
      undefined symbol: __pointer_chk_guard, version GLIBC_PRIVATE

The reporter hit it through an application probing the desktop theme
with gsettings: popen() returned nothing, the app silently used the
wrong theme, and mcpp said nothing. Verified locally — the 2.44 payload
kills /bin/sh, the 2.39 payload does not, which is why this survived as
long as it did and why "it works here" proved nothing.

The directory was published to serve a dlopen() the executable's
DT_NEEDED closure does not cover, and the artifact's RUNPATH already
covers exactly that: the link model emits -Wl,-rpath,<glibc> next to
--dynamic-linker, and the built binary's RUNPATH is byte-identical
before and after this change. So the environment entry bought nothing
and cost every child process. It is gone.

Measured against the released mcpp with the same fixture:
  before  LDLP=[…/runtime:…/xim-x-glibc/2.39/lib64]
  after   LDLP=[…/runtime]

The decision lives in mcpp.platform.runtime_env_contract as a scope,
not a condition: no build-level predicate can make an inherited variable
safe for a process mcpp did not launch and cannot see. mcpp#291 was the
same mistake one hop closer in.

208 pins all three halves — the dir is in the artifact RUNPATH, it is
not in the program's environment, and both the /bin/sh child and the
dlopen still work. The RUNPATH assertion deliberately checks coverage
mcpp does not itself emit, so losing it is a red test rather than a
silent loss of resolution.
The Linux closure verdict is cached against the artifact stat and the
RuntimeBinding contract hash, and caching a FAILURE is the half worth
pinning: an unchanged artifact that was proven wrong is still wrong, and
reporting success the second time tells the user a problem went away
when nothing about the program changed.

It is easy to lose by accident, because the project fast path exists to
skip work when nothing changed — including, unless something stops it,
the check that would have failed. `validated_artifact_snapshot` stops
it by refusing to engage unless every stored verdict is a PASS; that
rule had no test.

209 poisons one stored verdict without touching a source file and
requires the next build to fail with the recorded diagnostics, requires
`mcpp self doctor` to explain it from storage, and then requires a
rebuilt artifact to be re-validated — a mismatch is cached, not a
sentence, or the first real failure would wedge the project until
someone deleted target/.
`schedule` fires every six hours off the default branch and set
publish=true unconditionally. Merging this workflow was therefore enough
to make mcpp start writing to the AUR on its own, within six hours, on a
path that had never completed a real push — only a makepkg --verifysource
dry-run. Merging is a decision about code; publishing to a third-party
service is a decision about the outside world, and they should not be
the same act.

Both automatic triggers now plan and report but withhold the push unless
the repository variable AUR_AUTOPUBLISH is exactly "true", so a typo
fails closed. Dry runs still validate payloads, render .SRCINFO, query
the AUR and print the diff — only the push is withheld, so nothing is
lost but the surprise. workflow_dispatch keeps its explicit per-run
switch, which is how the first watched publish is meant to happen, and
unsetting the variable is a kill switch that needs no revert.

The contract test runs the workflow's own decision shell rather than a
paraphrase of it, so the guarantee cannot drift from the YAML.
Every test covering local-index addressing needed `gcc` or
`fresh-sandbox`, and Windows has neither capability. So the one platform
where path semantics actually differ was the one platform never
asserting them — which is why a fixture could write an MSYS path into
mcpp.toml and the failure surfaced four steps away, as "package not
found in any configured index", and was chased through the workspace
inheritance code that was not involved.

210 needs no compiler, no sandbox bootstrap and no network: it asserts
at the resolution layer, which is the layer that was broken. An absolute
[indices] path resolves; a workspace member inherits a root-relative one
and reads it; a miss in both reports the index as a READABLE root, since
"root absent" is the fingerprint of a path written in the wrong
spelling; and the route diagnostic that makes any of this findable stays
free of filesystem paths.
Two independent bumps that happen to land on the same date string.

The xlings pin moves from 2026.8.9.2 to the current stable 2026.8.10.1
across all 16 pin sites; check_version_pins.sh verifies them against
src/xlings.cppm rather than trusting a comment. Note that 2026.8.10.1
still declares a bare `ftxui`, so the four cross-repo CI failures on this
branch are resolved by the bare-name migration window in this PR, not by
the pin — openxlings/xlings#521 remains the correct upstream fix and is
no longer on the critical path.

The release version moves from 2026.8.9.1 to 2026.8.10.1 because the
version is a DATE and the date changed while this branch was in review.
2026.8.9.1 was never tagged, so nothing depends on the old string.

The release-manifest and AUR fixtures derive "some other version" from
VERSION; both halves move so they stay distinct instead of collapsing
onto the release version and silently asserting nothing.

Bootstrap pin stays at the released 2026.8.8.4 — it is the self-hosting
starting point, not a function of what this release will be.
Three things a user or a maintainer cannot discover from the code:

- The one-release exit ramp for namespace-omitted selectors: what still
  resolves, what the warning says, that the canonical identity is what
  reaches the lock, that `mcpp add` performs the migration, and that a
  stated namespace is not eligible. Written with the reason attached —
  every published `compat.*` package spells its dependency bare, so
  failing them outright would make a program release invalidate data
  that is already published and cannot be edited.

- Why the private libc directory is binary-scoped and never
  environment-scoped, as a table of blast radius rather than a rule to
  memorize: DT_RUNPATH reaches one object, LD_LIBRARY_PATH reaches every
  descendant process forever, and a GLIBC_PRIVATE-coupled libc handed to
  the host loader kills /bin/sh before main (#401).

- How to arm the AUR automatic triggers, including that the kill switch
  is unsetting a repository variable rather than reverting a commit.

English and Chinese both, plus the changelog entry for 2026.8.10.1.
@speak-agent

Copy link
Copy Markdown
Member

Checkpoint — 重新判定 Windows 阻塞点,并把跨仓库串行改成并行(70d2487

本轮按 .agents/docs/2026-08-10-pr400-completion-design.md 的 D1–D5 全量实施,并纳入 #401

1. Windows E2E 2/2 的根因不是 workspace 索引继承

PR 正文与交接文档此前都记为 step (14)(member 读不到根 [indices])。原始日志说的是 step (15):

error: package 'acme.util' not found in any configured index
  tried: (acme, util)
  route: local index 'acme': root absent, pkgs absent
FAIL: 12_add_command.sh (exit 2, 2.06s)

四条独立证据:

  1. 退出码是 2。step (14) 被 || { …; exit 1; } 包住,失败只能是 exit 1;exit 2 是 mcpp 自己的退出码经 set -e 直传,只有裸 "$MCPP" add … > /dev/null 会这样。
  2. step (14) 的失败横幅 workspace member could not read its root-owned local index 不在日志里(同一 harness 会显示脚本 stdout,例如 118 的 windows: depfile degradation reported as expected)。
  3. 失败前最后两条未捕获输出属于 step (5) 与 step (12);step (13)/(14) 全部被 $( ) 捕获。
  4. step (15) 的 fixture 写的是 acme = { path = "$TMP/myapp/index" }$TMP 来自 mktemp -d,在 Git Bash 下是 MSYS 路径 /tmp/tmp.XXXX。MSYS 只转换 argv/env,不转换文件内容;原生 mcpp.exe 把开头的 / 读成「当前盘根相对」,于是索引落到 C:\tmp\… —— 正是 root absent, pkgs absent

step (15) 是本分支新增的;step (14) 在 main 上一直存在且 main 的 Windows E2E 是绿的(run 31275102045)。

Wine 重放是假绿:Wine 默认把 Z: 映射到 /\tmp\… 落回真实 /tmp/…。Wine 能验证 PE 产物能跑,不能验证路径语义。

修法按「一类」而不是「一行」:新增 tests/e2e/_host_path.sh(MSYS 用 cygpath -m,相对路径原样返回),新增 00_fixture_path_hygiene.sh 作为 lint,并迁移全仓 24 个文件里的 40 处。大部分此前只是因为对应测试在 Windows 上因缺 capability 而 SKIP 才没暴露。

2. ed4cf64weakly_canonical → lexically_normal:已证明,保留

改动前(run 31318089536)与改动后(run 31321961040)的失败文本逐字相同,所以它没有修好任何东西,而 main 带着 weakly_canonical 是绿的。

按设计文档 D2,用符号链接用例做了真实 RED:把 weakly_canonical 换回去,unit/test_pm_index_route 失败(71 passed, 1 failed)。理由成立但与原描述不同 —— anchoring 回答的是「作者指的是哪个目录」,答案必须留在作者写下的那棵树里;weakly_canonical 会把经符号链接访问的 workspace 重定位到另一棵树,而 prepare 的报错里会打印这个路径。改动保留,并由该用例锁住。

同时把 EXPECT_EQ(path, …lexically_normal()) 换成能力断言 —— 断言路径拼写对任何产出该字符串的规则都成立,这正是它在 Windows 全红时依然绿着的原因。

3. 裸名精确化:加一个版本的过渡期(D1)

四个红 job(Linux integration / macOS LLVM / aarch64 / Windows toolchains)全是同一句 ftxui exact miss。这不是跨仓库外部边界:本仓自己的 mcpp.toml 也被迫把 gtest 挪到 [dev-dependencies.compat] 才能自举,而索引里 34 个 compat.* 包与所有既有用户 manifest 都写裸名 —— 修 xlings 帮不到他们。

「已发布的数据不得让程序失效」与「已发布的程序不得让既有数据失效」是同一条判据的两个方向。#278 修掉的缺陷是静默,不是回退本身。

实现:精确 (mcpplibs, name) 未命中后,且仅当 namespace 被省略时,再试 (compat, name) 与「不声明 namespace 的上游 descriptor」;命中打印弃用警告 + 可粘贴 manifest 片段,写入 lock/install/cache 的是规范身份mcpp add <裸名> 直接把规范形式写回 manifest。mcpplibs.gtest 写明了身份,未命中即失败;第三方 namespace 仍不可被裸名触达。2026.9 移除,常量 kBareNameFallbackRemovedIn 保证警告/文档/移除不漂移。

xlings #521 因此不再在发布关键路径上(它仍是正确的上游修复)。

4. #401:私有 glibc 不再进入进程环境

mcpp run 曾把私有 glibc 目录放进 LD_LIBRARY_PATH,而该变量被程序派生的每一个进程继承;/bin/sh宿主 loader 加载,于是死在重定位阶段:

sh: symbol lookup error: …/xim-x-glibc/2.44/lib64/libc.so.6:
    undefined symbol: __pointer_chk_guard, version GLIBC_PRIVATE

本地已复现:2.44 payload 杀死 /bin/sh,2.39 不会 —— 这就是它能活这么久、以及「我这儿是好的」毫无意义的原因。

该目录只为「可执行文件 DT_NEEDED 闭包覆盖不到的 dlopen」存在,而产物 RUNPATH 已经覆盖它(link model 在 --dynamic-linker 旁就发了 -Wl,-rpath,<glibc>;改动前后产物 RUNPATH 逐字节相同)。所以这条环境项没有收益,只有代价。

同一 fixture 实测:

LD_LIBRARY_PATH
已发布 mcpp …/runtime:…/xim-x-glibc/2.39/lib64
本分支 …/runtime

决策收敛在新模块 src/platform/runtime_env_contract.cppm,是作用域不是条件:没有任何构建期判据能让一个继承变量对 mcpp 没启动、也看不见的进程变安全。

5. AUR:自动触发需要显式布防(D3)

schedule 每 6 小时跑一次且无条件 publish=true —— 合并本 PR 本身就足以让 mcpp 在 6 小时内无人值守地对 AUR 写入,而这条路径此前只做过 makepkg --verifysource dry-run。两个自动触发现在都只 plan+report,除非仓库变量 AUR_AUTOPUBLISH 精确等于 true(写错即 fail closed);workflow_dispatch 保留逐次开关,这正是第一次「有人盯着的」发布该走的路。契约测试直接执行 workflow 自己的判定 shell,而不是它的复述。

6. 其他

  • D5:新增 209_runtime_verdict_never_reruns_green.sh —— 污染一条已存 verdict、不动任何源文件,下一次 mcpp build 必须仍然失败并打印存档诊断;mcpp self doctor 必须从存档解释;重建后必须重新求值(是缓存,不是判决)。
  • 覆盖盲区:身份/索引路由的 e2e 全部需要 gccfresh-sandbox,Windows 两者皆无 —— 路径语义真正不同的平台,恰好是从不断言它的平台。新增 210_local_index_addressing_on_every_host.sh,无编译器、无沙箱、无网络,三平台都跑。
  • 版本:xlings pin → 已发布的 2026.8.10.1(16 处 pin 由 check_version_pins.sh 机器校验);发布版本 2026.8.9.12026.8.10.1(版本号是日期,评审期间日期变了;2026.8.9.1 从未打过 tag)。自举 pin 保持已发布的 2026.8.8.4

本地验证

  • 完整 C++ unit/integration:73/73,0 fail。
  • test_aur_reconcile + test_release_manifest25/25
  • check_version_pins.sh、全部 workflow YAML 解析、git diff --check、隐私扫描:通过。
  • 新增/改动的 e2e 逐条通过:001242434409273262162208209210
  • 最新 HEAD 的完整 Linux e2e 全量跑正在进行,结果会单独回帖;在拿到终态之前不把任何一项记为通过

提交

Commit 内容
3a6770d e2e fixture 路径改用 host 拼写(含 helper + lint + 40 处迁移)
1c42c73 锁住 inherited index 的 lexical anchoring(符号链接 RED 已验证)
4c0b7b7 裸名过渡期
afba6f0 #401:私有 libc 退出进程环境
1c3078e verdict 缓存不得靠重跑变绿
ead4933 AUR 自动触发需要布防
3bb2772 三平台本地索引寻址覆盖
d242922 发布状态:2026.8.10.1 / xlings 2026.8.10.1
70d2487 文档

Pinning to the current stable 2026.8.10.1 turned every Linux job red at
the same place: a COLD home cannot install xim:gcc@16.1.0 at all.

  Downloading xim:glibc@2.44 …
  integrity: cleaning incomplete install: …/xpkgs/xim-x-gcc/16.1.0
  [gcc] failed: config hook failed
  [xim:xpkg]: glibc payload not found, but gcc needs it to rewrite its
              ELF interpreter away from the build machine's path.
              install it first: xlings install xim:glibc@2.39

The version it installs and the version the hook asks for disagree, and
gcc.lua declares `xim:glibc@>=2.39` so resolving 2.44 is legal — the
failure is that the consumer's config hook cannot find the dependency
payload that was just fetched.

A/B on the same workflow, same runner image, both with a confirmed
cache miss, so this is not a long-standing cold-start bug that warm
caches were hiding:

  2026.8.9.2  cold  → gcc installs      (run 31317627461)
  2026.8.10.1 cold  → the failure above (run 31335075557, 4/4 Linux jobs)

Nothing in mcpp can route around it: it is upstream install ordering or
store-root lookup, and it breaks every new machine, every cold CI cache
and every fresh install. Reported with the evidence as
openxlings/xlings#524; the pin moves once that ships.

The release version stays 2026.8.10.1 — it is a date, and this is a
different project's version that happens to share the string.
The design that drove this round of work, plus an appendix of outcomes
written after the fact — including the two places reality diverged from
the plan.

D2 resolved as "proven, keep", but for a different reason than the
commit it was defending claimed: not a Windows short-name alias, but
that anchoring must not relocate an index out of the tree the workspace
author addressed. D5's invariant turned out to already hold; it simply
had no test.

Two things arrived unplanned: #401, and the discovery that every
identity/index-routing e2e was gated behind a capability Windows does
not have — so the platform where path semantics differ was the one
platform never asserting them.

And main line C's bottleneck moved. The migration window took
openxlings/xlings#521 off the critical path as intended, but pinning to
the newest xlings put a harder one in its place: a cold home cannot
install gcc at all (openxlings/xlings#524). Recorded with the A/B,
because the lesson generalizes — "pin to latest" is an action that needs
verifying, not a text substitution, and it only surfaced here because
changing the pin also changed the CI cache key.
166 exists for mcpp#291 and had two halves. The negative half — a plain
binary must not be handed the private glibc — is still exactly right and
is unchanged. The positive half required the opposite in the presence of
a dlopen-reachable dependency: the payload dir HAD to be on
LD_LIBRARY_PATH, "because dlopen'd libraries do not consult the
executable's RUNPATH".

The guard was right to exist. It stopped anyone from "fixing" #291 by
deleting the entry and quietly breaking dlopen. But it pinned the
mechanism rather than the capability, and the mechanism's premise was
false: a dlopen() performed by the executable does consult the
executable's DT_RUNPATH, and the link model already puts the payload
glibc there — the artifact's RUNPATH is byte-identical with and without
the environment entry. So the entry bought nothing while reaching every
descendant process, which is how it became mcpp#401.

The capability it was protecting now lives in 208, which asserts the
dlopen actually resolves. 166 keeps the rule it is named for and now
applies it to both shapes, plus one new assertion: the project's own
[runtime] library_dirs entry must still be there. Only the private libc
is binary-scoped; dropping ordinary runtime dirs too would be a
different regression that the negative assertion alone would not catch.

Full local Linux e2e on this HEAD: 202 passed, 0 failed, 8 skipped.
@speak-agent

Copy link
Copy Markdown
Member

Checkpoint — latest-head 全矩阵终态全绿(94dbf19),18/18

pass  bare Windows: no Visual Studio (windows x64)
pass  build + test + package (windows x64, self-host)
pass  build + unit tests (linux x86_64, self-host)
pass  cross-build aarch64-linux-musl (mcpp + xlings)
pass  e2e 1/2 (linux x86_64, self-host)      pass  e2e 1/2 (windows x64, self-host)
pass  e2e 2/2 (linux x86_64, self-host)      pass  e2e 2/2 (windows x64, self-host)
pass  e2e suite (macOS ARM64, self-host)     pass  hermetic e2e (no host toolchain, container)
pass  integration: mcpp builds & runs xlings pass  macOS ARM64 — xlings LLVM end-to-end
pass  mingw-cross linux→windows (build + wine run)
pass  toolchain: gcc (cold self-host)        pass  toolchain: musl + llvm
pass  toolchains + regressions (windows x64)
pass  windows→linux artefact really runs (linux)
pass  windows→linux cross-build (windows host)

四个此前红的 ftxui exact-miss job(Linux integration / macOS LLVM / aarch64 / Windows toolchains)
裸名迁移窗口转绿——不是靠恢复静默回退,而是靠「带完整身份的弃用警告 + 规范身份进 lock」。
xlings #521 仍是正确的上游修复,但已不在关键路径上。

Windows E2E 2/2 由 fixture 路径卫生转绿:_host_path.sh + 00_fixture_path_hygiene.sh lint +
全仓 24 文件 40 处迁移。

本地验证(最终 HEAD)

  • C++ unit/integration:73/73
  • 完整 Linux E2E:202 passed / 0 failed / 8 skipped
  • python 契约(AUR + release manifest):25/25
  • check_version_pins.sh、全部 workflow YAML、git diff --check、隐私扫描:通过

途中 166_run_env_no_private_glibc.sh 抓到了 #401 的改动 —— 它断言「私有 glibc 必须在
LD_LIBRARY_PATH 上」。那条守卫本身是对的(防止有人「修 #291」时顺手把 dlopen 弄坏),
但它锁的是机制而不是能力,而它的前提是错的:可执行文件自己发起的 dlopen 会查它自己的
DT_RUNPATH,link model 早就把 payload glibc 放进去了(实测:删掉那条环境项,产物 RUNPATH
逐字节相同)。能力断言已移到 208166 保留它命名的那条规则,并对两种形状都断言。

隔离 fresh-home 生态验收

HOME/MCPP_HOME/XLINGS_HOME 全部指向临时目录的冷启动:

结果
冷 sandbox bootstrap PASS
mcpp new(默认模板)/ --list-templates / 未知模板被拒 PASS
冷工具链下 mcpp build / run / test PASS
裸名迁移窗口(警告命名 compat.gtest PASS
mcpp add 把裸名迁成 [dev-dependencies.compat] PASS
#401mcpp run/bin/sh 子进程存活 PASS
#401:私有 glibc 不在程序环境里 PASS(LDLP=[<proj>/runtime]
精确 dotted 模板 selector NOT_EXERCISED(本次 listing 里没有点分 selector)

图形栈验收:mcpp 侧全绿,缺口在 xlings/xim

隔离 home 里 xlings install -y graphics 成功(NVIDIA 550.144.03,35 包),
GLFW + OpenGL 程序构建通过,mcpp why runtime 完整记录:

  • requirement / provider / provenance(index+compat@2026.08.08)/ link intent 全部正确
  • SubOS 图形环境确实被应用__EGL_VENDOR_LIBRARY_DIRSLIBGL_DRIVERS_PATH 指向 SubOS
  • post-link 闭包校验 pass(含 12 个随产物部署的 X 库)
  • why runtime 只读存档,不重新探测宿主

运行时仍拿不到 GLXFBConfig。定位:GLX vendor 库装在 SubOS 里
<subos>/lib/libGLX_nvidia.so.0)但 payload 的 libGLX.so.0 在 dlopen 时够不到
——
SubOS 只声明了 EGL vendor 目录,没有 GLX 侧的等价物。

而且不能用 LD_LIBRARY_PATH 兜:

$ LD_LIBRARY_PATH=<subos>/lib timeout 600 mcpp run
timeout: symbol lookup error: <subos>/lib/libc.so.6:
         undefined symbol: __pointer_chk_guard, version GLIBC_PRIVATE

——#401 被手工复现了一次,也正好印证了 xim 自己在安装时打印的那条警告
(“Prefer RPATH on the consumer”)。按冻结的设计边界,driver/ICD 归 xlings/xim,
已带完整证据上报 openxlings/xlings#525

mcpp 在这条链上没有待办:它消费的是规范化后的 provider 事实,没有探测 GPU/driver,
诊断出口也正确地指向 xlings doctor

跨仓库

按用户明确授权,接下来 squash + bypass 合入并发布 v2026.8.10.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: converge template selectors, runtime binding, graphics contract, and mcpp-bin AUR

2 participants